package com.fsm.template.serviceImpl;

import com.fsm.common.tools.Result.Result;
import com.fsm.template.dto.Field;
import com.fsm.template.dto.TableDto;
import com.fsm.template.mapper.CreateTableMapper;
import com.fsm.template.service.CreateTemplateService;
import com.mybatisflex.codegen.Generator;
import com.mybatisflex.codegen.config.EntityConfig;
import com.mybatisflex.codegen.config.GlobalConfig;
import com.mybatisflex.core.util.CollectionUtil;
import com.zaxxer.hikari.HikariDataSource;
import lombok.extern.slf4j.Slf4j;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.StringWriter;
import java.util.*;

@Service
@Slf4j
public class CreateTemplateServiceImpl implements CreateTemplateService {
    @Autowired
    private CreateTableMapper createTableMapper;

    @Value("${mapper_create_path}")
    private String strPath;

    @Value("${java_create_path}")
    private String strPathCom;

    @Value("${vue_create_path}")
    private String strVuePath;

    @Value("${vmPath}")
    private String vmPath;

    @Value("${webPath}")
    private String webPath;

    @Value("${spring.datasource.username}")
    private String userName;

    @Value("${spring.datasource.password}")
    private String password;

    @Value("${spring.datasource.url}")
    private String url;


    private VelocityEngine getVelocityEngine(){
        VelocityEngine velocityEngine = new VelocityEngine();
        velocityEngine.setProperty("file.resource.loader.path",vmPath);
        velocityEngine.init();
        return velocityEngine;
    }
    /**
     * 生成模版接口
     *
     * @param
     * @return
     */
    @Override
    public Result createVue(TableDto tableDto) throws Exception {
        if(CollectionUtil.isNotEmpty(tableDto.getTableDtoList())){
            List<TableDto> tableDtoList = tableDto.getTableDtoList();
            for(TableDto tableDtoOne : tableDtoList){
                List<Field> columTable = createTableMapper.selectColum(tableDtoOne.getTableName());
                //重组
                Map<String,Field> columTableMap = new HashMap<>();
                for(Field field : columTable){
                    field.setFieldName(convertToField(field.getField()));
                    field.setTableName(convertToJave(field.getField()));
                    field.setTableDefName(field.getField().toUpperCase());
                    columTableMap.put(field.getField(),field);
                }
                //处理索引字段集合
                List<Field> indexColumTable = new ArrayList<>();
                for(String field : tableDtoOne.getIndexFieldList()){
                    indexColumTable.add(columTableMap.get(field));
                }
                //创建基本的Mapper文件和Xml文件以及TableDef文件
                create_table(tableDtoOne.getTableName(),tableDtoOne.getPath());
                //生成vue文件模版
                createTemplateVue(columTable,tableDtoOne.getTableCommon(),tableDtoOne.getTableName());
                //生成JS文件模版
                createTemplateJs(columTable,tableDtoOne.getTableCommon(),tableDtoOne.getTableName());
                //生成Controller文件模版
                createTemplateController(columTable,tableDtoOne.getTableCommon(),tableDtoOne.getTableName(),tableDtoOne.getPath(),tableDtoOne.getIsAddLog(),tableDtoOne.getSystemType());
                //生成Service文件模版
                createTemplateService(columTable,tableDtoOne.getTableCommon(),tableDtoOne.getTableName(),tableDtoOne.getPath());
                //生成ServiceImpl文件模版
                createTemplateServiceImpl(columTable,tableDtoOne.getTableCommon(),tableDtoOne.getTableName(),tableDtoOne.getPath(),indexColumTable);
                //生成Entity文件模版
                createTemplateEntity(columTable,tableDtoOne.getTableCommon(),tableDtoOne.getTableName(),tableDtoOne.getPath());
                //生成Entity表格文件模版
                createTemplateBo(columTable,tableDtoOne.getTableCommon(),tableDtoOne.getTableName(),tableDtoOne.getPath());
            }
            return Result.ok();
        }else{
            List<Field> columTable = createTableMapper.selectColum(tableDto.getTableName());
            //重组
            Map<String, Field> columTableMap = new HashMap<>();
            for(Field field : columTable){
                field.setFieldName(convertToField(field.getField()));
                field.setTableName(convertToJave(field.getField()));
                field.setTableDefName(field.getField().toUpperCase());
                columTableMap.put(field.getField(),field);
            }
            //处理索引字段集合
            List<Field> indexColumTable = new ArrayList<>();
            for(String field : tableDto.getIndexFieldList()){
                indexColumTable.add(columTableMap.get(field));
            }
            //创建基本的Mapper文件和Xml文件以及TableDef文件
            create_table(tableDto.getTableName(),tableDto.getPath());
//            //生成vue文件模版
//            createTemplateVue(columTable,tableDto.getTableCommon(),tableDto.getTableName());
//            //生成JS文件模版
//            createTemplateJs(columTable,tableDto.getTableCommon(),tableDto.getTableName());
            //生成Controller文件模版
            createTemplateController(columTable,tableDto.getTableCommon(),tableDto.getTableName(),tableDto.getPath(),tableDto.getIsAddLog(),tableDto.getSystemType());
            //生成Service文件模版
            createTemplateService(columTable,tableDto.getTableCommon(),tableDto.getTableName(),tableDto.getPath());
            //生成ServiceImpl文件模版
            createTemplateServiceImpl(columTable,tableDto.getTableCommon(),tableDto.getTableName(),tableDto.getPath(),indexColumTable);
            //生成Entity文件模版
            createTemplateEntity(columTable,tableDto.getTableCommon(),tableDto.getTableName(),tableDto.getPath());
            //生成Entity表格文件模版
            createTemplateBo(columTable,tableDto.getTableCommon(),tableDto.getTableName(),tableDto.getPath());
            return Result.ok();
        }
    }
    /**
     * 生成ExcelBo模版页面
     * @return
     */
    public void createTemplateBo(List<Field> columTable,String tableCommon,String tableName,String path){
        VelocityEngine velocityEngine = getVelocityEngine();
        Template template = velocityEngine.getTemplate("excelboTemplate.vm", "UTF-8");
        String fileName = convertToJave(tableName);
        VelocityContext data = new VelocityContext();
        String tableNameMessage = convertToCamelCase(tableName);
        data.put("DbTableName", tableName);
        data.put("fileName", fileName);
        data.put("tableCommon", tableCommon);
        data.put("tableName", tableNameMessage);
        data.put("columTable", columTable);
        data.put("path",path);
        StringWriter stringWriter = new StringWriter();
        long curr = System.currentTimeMillis();
        template.merge(data, stringWriter);
        long end = System.currentTimeMillis();
        log.info("Entity文件模板初始化:{}ms",(end-curr));
        String toString = stringWriter.toString();
        File file = new File(strPathCom + "\\entity\\excelbo");
        if(!file.exists()){
            file.mkdir();
        }
        String filePath = strPathCom + "\\entity\\excelbo\\" +  fileName + "Bo.java";
        File newFile = new File(filePath);
        if(newFile.exists()){
            newFile.delete();
        }
        saveAsFileWriter(toString,filePath);
    }
    /**
     * 生成Entity模版页面
     * @return
     */
    public void createTemplateEntity(List<Field> columTable,String tableCommon,String tableName,String path){
        VelocityEngine velocityEngine = getVelocityEngine();
        Template template = velocityEngine.getTemplate("entityTemplate.vm", "UTF-8");
        String fileName = convertToJave(tableName);
        VelocityContext data = new VelocityContext();
        String tableNameMessage = convertToCamelCase(tableName);
        data.put("DbTableName", tableName);
        data.put("fileName", fileName);
        data.put("tableCommon", tableCommon);
        data.put("tableName", tableNameMessage);
        data.put("columTable", columTable);
        data.put("path",path);
        StringWriter stringWriter = new StringWriter();
        long curr = System.currentTimeMillis();
        template.merge(data, stringWriter);
        long end = System.currentTimeMillis();
        log.info("Entity文件模板初始化:{}ms",(end-curr));
        String toString = stringWriter.toString();
        File file = new File(strPathCom + "\\entity");
        if(!file.exists()){
            file.mkdir();
        }
        String filePath = strPathCom + "\\entity\\" +  fileName + ".java";
        File newFile = new File(filePath);
        if(newFile.exists()){
            newFile.delete();
        }
        saveAsFileWriter(toString,filePath);
    }

    /**
     * 生成ServiceImpl模版页面
     * @return
     */
    public void createTemplateServiceImpl(List<Field> columTable,String tableCommon,String tableName,String path,List<Field> indexColumTable){
        VelocityEngine velocityEngine = getVelocityEngine();
        Template template = velocityEngine.getTemplate("ServiceImplTemplate.vm", "UTF-8");
        String fileName = convertToJave(tableName);
        VelocityContext data = new VelocityContext();
        String tableNameMessage = convertToCamelCase(tableName);
        data.put("fileName", fileName);
        data.put("tableCommon", tableCommon);
        data.put("tableName", tableNameMessage);
        data.put("columTable", columTable);
        data.put("path",path);
        data.put("indexColumTable",indexColumTable);
        data.put("tableDefName",tableName.toUpperCase());
        StringWriter stringWriter = new StringWriter();
        long curr = System.currentTimeMillis();
        template.merge(data, stringWriter);
        long end = System.currentTimeMillis();
        log.info("ServiceImpl文件模板初始化:{}ms",(end-curr));
        String toString = stringWriter.toString();
        File file = new File(strPathCom + "\\serviceImpl");
        if(!file.exists()){
            file.mkdir();
        }
        String filePath = strPathCom + "\\serviceImpl\\" +  fileName + "ServiceImpl.java";
        File newFile = new File(filePath);
        if(newFile.exists()){
            newFile.delete();
        }
        saveAsFileWriter(toString,filePath);
    }

    /**
     * 生成Service模版页面
     * @return
     */
    public void createTemplateService(List<Field> columTable,String tableCommon,String tableName,String path){
        VelocityEngine velocityEngine = getVelocityEngine();
        Template template = velocityEngine.getTemplate("ServiceTemplate.vm", "UTF-8");
        String fileName = convertToJave(tableName);
        VelocityContext data = new VelocityContext();
        String tableNameMessage = convertToCamelCase(tableName);
        data.put("fileName", fileName);
        data.put("tableCommon", tableCommon);
        data.put("tableName", tableNameMessage);
        data.put("columTable", columTable);
        data.put("path",path);
        StringWriter stringWriter = new StringWriter();
        long curr = System.currentTimeMillis();
        template.merge(data, stringWriter);
        long end = System.currentTimeMillis();
        log.info("Service文件模板初始化:{}ms",(end-curr));
        String toString = stringWriter.toString();
        File file = new File(strPathCom + "\\service");
        if(!file.exists()){
            file.mkdir();
        }
        String filePath = strPathCom + "\\service\\" +  fileName + "Service.java";
        File newFile = new File(filePath);
        if(newFile.exists()){
            newFile.delete();
        }
        saveAsFileWriter(toString,filePath);
    }

    /**
     * 生成Controller模版页面
     * @return
     */
    public void createTemplateController(List<Field> columTable,String tableCommon,String tableName,String path,Boolean isAddLog,String systemType){
        VelocityEngine velocityEngine = getVelocityEngine();
        Template template = velocityEngine.getTemplate("ControllerTemplate.vm", "UTF-8");
        String fileName = convertToJave(tableName);
        VelocityContext data = new VelocityContext();
        String tableNameMessage = convertToCamelCase(tableName);
        data.put("fileName", fileName);
        data.put("tableCommon", tableCommon);
        data.put("tableName", tableNameMessage);
        data.put("columTable", columTable);
        data.put("isAddLog", isAddLog);
        data.put("systemType", systemType);
        data.put("path",path);
        StringWriter stringWriter = new StringWriter();
        long curr = System.currentTimeMillis();
        template.merge(data, stringWriter);
        long end = System.currentTimeMillis();
        log.info("Controller文件模板初始化:{}ms",(end-curr));
        String toString = stringWriter.toString();
        File file = new File(strPathCom + "\\controller");
        if(!file.exists()){
            file.mkdir();
        }
        String filePath = strPathCom + "\\controller\\" +  fileName + "Controller.java";
        File newFile = new File(filePath);
        if(newFile.exists()){
            newFile.delete();
        }
        saveAsFileWriter(toString,filePath);
    }




    /**
     * 生成vue模版页面
     * @return
     */
    public void createTemplateVue(List<Field> columTable,String tableCommon,String tableName){
        VelocityEngine velocityEngine = getVelocityEngine();
        Template template = velocityEngine.getTemplate("vueTemplate.vm", "UTF-8");
        VelocityContext data = new VelocityContext();
        String tableNameMessage = convertToCamelCase(tableName);
        String fileName = convertToJave(tableName);
        data.put("fileName", fileName);
        data.put("tableCommon", tableCommon);
        data.put("tableName", tableNameMessage);
        data.put("columTable", columTable);
        StringWriter stringWriter = new StringWriter();
        long curr = System.currentTimeMillis();
        template.merge(data, stringWriter);
        long end = System.currentTimeMillis();
        log.info("Vue文件模板初始化:{}ms",(end-curr));
        String toString = stringWriter.toString();
        File file = new File(webPath + "view\\"+  tableNameMessage);
        if(!file.exists()){
            file.mkdir();
        }
        String filePath = webPath  + "view\\"+ tableNameMessage  + "\\index.vue";
        File newFile = new File(filePath);
        if(newFile.exists()){
            newFile.delete();
        }
        saveAsFileWriter(toString,webPath + "view\\"+ tableNameMessage  + "\\index.vue");
        System.out.println("VUE文件路径：" + webPath + "view\\"+ tableNameMessage  + "\\index.vue");
    }

    /**
     * 生成Js模版页面
     * @return
     */
    public void createTemplateJs(List<Field> columTable,String tableCommon,String tableName){
        VelocityEngine velocityEngine = getVelocityEngine();
        Template template = velocityEngine.getTemplate("vueTemplateJs.vm", "UTF-8");
        VelocityContext data = new VelocityContext();
        String tableNameMessage = convertToCamelCase(tableName);
        data.put("tableCommon", tableCommon);
        data.put("tableName", tableNameMessage);
        StringWriter stringWriter = new StringWriter();
        long curr = System.currentTimeMillis();
        template.merge(data, stringWriter);
        long end = System.currentTimeMillis();
        log.info("Js文件模板初始化:{}ms",(end-curr));
        String toString = stringWriter.toString();
        File file = new File(webPath + "api\\"+  tableNameMessage + ".js");
        if(!file.exists()){
            file.mkdir();
        }
        String filePath = webPath + "api\\"+  tableNameMessage + ".js";
        File newFile = new File(filePath);
        if(newFile.exists()){
            newFile.delete();
        }
        saveAsFileWriter(toString,  webPath + "api\\"+  tableNameMessage + ".js");
        System.out.println("JS文件路径：" + webPath + "api\\"+  tableNameMessage + ".js");
    }

    /**
     * 创建表
     */
    void create_table(String tableName,String path) {
        long curr = System.currentTimeMillis();
        //配置数据源
        HikariDataSource dataSource = new HikariDataSource();
        dataSource.setJdbcUrl(url);
        dataSource.setUsername(userName);
        dataSource.setPassword(password);
        //创建配置内容，两种风格都可以。
        GlobalConfig globalConfig = createGlobalConfigUseStyle1(tableName,path);
        //通过 datasource 和 globalConfig 创建代码生成器
        Generator generator = new Generator(dataSource, globalConfig);
        //生成代码
        generator.generate();
        long end = System.currentTimeMillis();
        log.info("Mapper文件模板初始化:{}ms",(end-curr));
    }

    private GlobalConfig createGlobalConfigUseStyle1(String tableName,String path) {
        //创建配置内容
        GlobalConfig globalConfig = new GlobalConfig();
        Set<String> tables = new HashSet<>();
        tables.add(tableName);
        globalConfig.setGenerateTables(tables);
        //设置表前缀和只生成哪些表
        globalConfig.setSourceDir(strPath);
        //设置根包
        globalConfig.setBasePackage(path);
        //注释配置
        globalConfig.getJavadocConfig()
                .setAuthor("")
                .setSince("");
        globalConfig.setEntityWithLombok(false);
        //设置生成 entity
        globalConfig.setEntityGenerateEnable(false);
        //设置生成 controller
        globalConfig.setControllerGenerateEnable(false);
        //设置生成 Service
        globalConfig.setServiceGenerateEnable(false);
        //设置生成 ServiceImpl
        globalConfig.setServiceImplGenerateEnable(false);
        //设置生成 mapper
        globalConfig.setMapperGenerateEnable(true);
        //设置生成 mapperXml
        globalConfig.setMapperXmlGenerateEnable(true);
        //设置生成TableDef
        globalConfig.setTableDefGenerateEnable(true);
        globalConfig.enableEntity()
                .setWithLombok(true)
                .setOverwriteEnable(false)
                .setWithLombok(true)
                .setWithSwagger(true)
                .setSwaggerVersion(EntityConfig.SwaggerVersion.DOC);
        // 开启 Mapper 生成配置
        globalConfig.getMapperConfig()
                .setClassSuffix("Mapper")
                .setOverwriteEnable(false)
                .setMapperAnnotation(true);
        // 开启 Service 生成配置
        globalConfig.getServiceConfig()
                .setClassSuffix("Service")
                .setOverwriteEnable(false);
        // 开启 ServiceImpl  生成配置
        globalConfig.getServiceImplConfig()
                .setOverwriteEnable(false);
        //Controller 生成配置
        globalConfig.getControllerConfig()
                .setOverwriteEnable(false);
        // TableDef 生成配置
        globalConfig.getTableDefConfig()
                .setClassSuffix("Table")
                .setOverwriteEnable(true);
        // MapperXml 生成配置
        globalConfig.getMapperConfig()
                .setOverwriteEnable(false);
        return globalConfig;
    }


    /**
     * 将下划线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空，则返回空字符串
     * t_com_user --> TComUser
     *
     * @param name 转换前的下划线大写方式命名的字符串
     * @return 转换后的驼峰式命名的字符串
     */
    public static String convertToJave(String name) {
        StringBuilder result = new StringBuilder();
        // 快速检查
        if (name == null || name.isEmpty()) {
            // 没必要转换
            return "";
        } else if (!name.contains("_")) {
            // 不含下划线，仅将首字母大写
            return name.substring(0,1).toUpperCase() + name.substring(1);
        }
        // 用下划线将原始字符串分割
        String[] camels = name.split("_");
        for (String camel : camels) {
            // 跳过原始字符串中开头、结尾的下换线或双重下划线
            if (camel.isEmpty()) {
                continue;
            }
            // 首字母大写
            result.append(camel.substring(0, 1).toUpperCase());
            result.append(camel.substring(1).toLowerCase());
        }
        StringBuilder ret = new StringBuilder(result.substring(0, 1).toUpperCase());
        ret.append(result.substring(1, result.toString().length()));
        return ret.toString();
    }
    /**
     * 将下划线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空，则返回空字符串
     * t_com_user --> tComUser
     *
     * @param name 转换前的下划线大写方式命名的字符串
     * @return 转换后的驼峰式命名的字符串
     */
    public static String convertToCamelCase(String name) {
        StringBuilder result = new StringBuilder();
        // 快速检查
        if (name == null || name.isEmpty()) {
            // 没必要转换
            return "";
        } else if (!name.contains("_")) {
            // 不含下划线，仅将首字母大写
            return name.substring(0,1).toUpperCase() + name.substring(1);
        }
        // 用下划线将原始字符串分割
        String[] camels = name.split("_");
        for (String camel : camels) {
            // 跳过原始字符串中开头、结尾的下换线或双重下划线
            if (camel.isEmpty()) {
                continue;
            }
            // 首字母大写
            result.append(camel.substring(0, 1).toUpperCase());
            result.append(camel.substring(1).toLowerCase());
        }
        StringBuilder ret = new StringBuilder(result.substring(0, 1).toLowerCase());
        ret.append(result.substring(1, result.toString().length()));
        return ret.toString();
    }

    /**
     * 将下划线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空，则返回空字符串
     * t_com_user --> tComUser
     * user --> user
     *
     * @param name 转换前的下划线大写方式命名的字符串
     * @return 转换后的驼峰式命名的字符串
     */
    public static String convertToField(String name) {
        StringBuilder result = new StringBuilder();
        // 快速检查
        if (name == null || name.isEmpty()) {
            // 没必要转换
            return "";
        } else if (!name.contains("_")) {
            // 不含下划线，仅将首字母大写
            return name;
        }
        // 用下划线将原始字符串分割
        String[] camels = name.split("_");
        for (String camel : camels) {
            // 跳过原始字符串中开头、结尾的下换线或双重下划线
            if (camel.isEmpty()) {
                continue;
            }
            // 首字母大写
            result.append(camel.substring(0, 1).toUpperCase());
            result.append(camel.substring(1).toLowerCase());
        }
        StringBuilder ret = new StringBuilder(result.substring(0, 1).toLowerCase());
        ret.append(result.substring(1, result.toString().length()));
        return ret.toString();
    }

    //生成vue文件
    private static void saveAsFileWriter(String content,String filePath) {
        FileWriter fwriter = null;
        try {
            // true表示不覆盖原来的内容，而是加到文件的后面。若要覆盖原来的内容，直接省略这个参数就好
            fwriter = new FileWriter(filePath, true);
            fwriter.write(content);
        } catch (IOException ex) {
            ex.printStackTrace();
        } finally {
            try {
                fwriter.flush();
                fwriter.close();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    }

}
